#!/usr/lib/python

import commonLib
import os
import pickle
import math

def generateDeltaTableInOrderToPutNodeAtPosition(node,position,deltaTableLen):
	position = position + 1 #HACK
	#Nodes is an array of nodes that we want to put after the node #position
	deltaTable = []
	for i in range(0,deltaTableLen):
		deltaTable.append(commonLib.Node(i))
		deltaTable[i].delta = 1
		deltaTable[i].parents.append(-1)
	#We now have a table that does a regular boot. Let's modify it
	#in order to put the nodes we want at the position we want
	tmpRow = commonLib.Node(4)#Just to create a new instance
	tmpRow = deltaTable[node.name]
	deltaTable.remove(tmpRow)
	deltaTable.insert(position,tmpRow)
	if len(node.parents) > 0:
		for i,parent in enumerate(node.parents):
			if (parent != -1):
				if parent >= position + 1:
					tmpRow = deltaTable[int(parent)+1+i]
					deltaTable.remove(deltaTable[int(parent)+1+i])
					deltaTable.insert(position,tmpRow)

	#Then reverse the array
	deltaTable.reverse()
	
	return deltaTable

def restore(nbr):
	#Open the restore file, deserialize and them jump to the right function
	pwd = os.getcwd()	
	f = open(pwd+'/restore'+nbr,'r')
	content = f.readlines()
	f.close()
	partNbr  = int(content[0])

	if partNbr == 1:
		nbrOfInitCalls = int(content[1])
		serialNumber = int(content[2])
		serialized = ""
		for i in range(3,len(content)):
			serialized += content[i]
		NodeCollection = pickle.loads(serialized)
		mySerialPortInterface = commonLib.serialPortInterface(serialNumber)
		findDependencies(nbrOfInitCalls,mySerialPortInterface,NodeCollection)
		findLastDependencies(NodeCollection,mySerialPortInterface)	
	else:
		nbrOfInitCalls = int(content[1])
		positionInTheLoop = int(content[2])
		serialNumber = int(content[3])
		serialized = ""
		for i in range(4,len(content)):
			serialized += content[i]
		NodeCollection = pickle.loads(serialized)
		mySerialPortInterface = commonLib.serialPortInterface(serialNumber)
		findLastDependencies(NodeCollection,mySerialPortInterface,positionInTheLoop)
	return NodeCollection
#It's ugly but let's use two global variables since it's way more simple
lastWorking = 0 
lastNotWorking = 0

def findDependenciesOfNode(Node,position,nbrOfInitCalls,NodeCollection,mySerialPortInterface):
	loadingAddress = commonLib.getOptions()['loadingAddress']
	global lastWorking, lastNotWorking
	if lastWorking-lastNotWorking != 1:
		deltaTable = generateDeltaTableInOrderToPutNodeAtPosition(Node,position,nbrOfInitCalls)
		commonLib.generateKernelCode('PARALLEL','YES',deltaTable,2)
		commonLib.compileKernel()
		command = "net init;load -b tftp://"+commonLib.getHostIP()+"/vmlinux.bin "+loadingAddress+";go\n" 
		print command
		mySerialPortInterface.writeCommand(command)		
		
		ret = mySerialPortInterface.analyzeInputStream('errorDetection')
		
		if ret == 0: #Everything was fine so try to go a further
			lastWorking = position
			position = math.floor((lastWorking+lastNotWorking)/2)
			findDependenciesOfNode(Node,position, nbrOfInitCalls,NodeCollection,mySerialPortInterface)
		else: #We had a problem so modify the left bound
			lastNotWorking = position 
			position = math.floor((lastWorking+lastNotWorking)/2)
			findDependenciesOfNode(Node,position,nbrOfInitCalls,NodeCollection,mySerialPortInterface)
	elif lastWorking - lastNotWorking == 1 :
		if lastNotWorking == 0:
			deltaTable = generateDeltaTableInOrderToPutNodeAtPosition(Node,0, nbrOfInitCalls)
			commonLib.generateKernelCode('PARALLEL','YES',deltaTable,2)
			commonLib.compileKernel()
			command = "net init; load -b tftp://"+commonLib.getHostIP()+"/vmlinux.bin "+loadingAddress+";go\n" 
			mySerialPortInterface.writeCommand(command)		
		
			ret = mySerialPortInterface.analyzeInputStream('errorDetection')
			if ret == 0:
				Node.parents.append(-1)
			else:
				Node.parents.append(2)
		else:
			if lastWorking == Node.name:
				lastWorking = lastWorking - 1		
			Node.parents.append(int(lastWorking))
			#Ugly but working
			for nodeName in NodeCollection[int(lastWorking)].parents:
				if int(nodeName) != -1:
					Node.parents.append(int(nodeName))
			cleanNodeCollection(NodeCollection)
			print Node.parents
			lastNotWorking = 0
			findDependenciesOfNode(Node,math.floor(lastWorking/2),nbrOfInitCalls,NodeCollection,mySerialPortInterface)

def findDependencies(numberOfInitCalls,mySerialPortInterface, NodeCollection=0):
	#Here is the most important function. It implements
	#the dichotomy algorithm used to find the dependencies
	global lastWorking, lastNotWorking
	if NodeCollection != 0:
		startIndex = len(NodeCollection)
	else:
		NodeCollection = []
		NodeCollection.append(commonLib.Node(0))
		NodeCollection[0].parents.append(-1)
		NodeCollection.append(commonLib.Node(1))
		NodeCollection[1].parents.append(-1)#The actual independency of this node will be checked later
		startIndex = 2
	#Then apply the dichotomy algorithm each one of the node
	for i in range(startIndex,numberOfInitCalls):
		newNode = commonLib.Node(i)
		lastWorking = i
		lastNotWorking = 0
		findDependenciesOfNode(newNode,math.floor(i/2),numberOfInitCalls,NodeCollection,mySerialPortInterface)
		print "Dependencies of "+str(newNode.name)+" are"
		print newNode.parents
		NodeCollection.append(newNode)
		saveCurrentStatus(1,NodeCollection,numberOfInitCalls,mySerialPortInterface)
	return NodeCollection
		
def findLastDependencies(NodeCollection,mySerialPortInterface, positionInTheLoop = 1):
		loadingAddress = commonLib.getOptions()['loadingAddress']
		for i in range(positionInTheLoop,len(NodeCollection)-1):
			if NodeCollection[i].parents[0] == -1:
				deltaTable = generateDeltaTableInOrderToPutNodeAtPosition(NodeCollection[i],-1,len(NodeCollection))
				commonLib.generateKernelCode('PARALLEL','YES',deltaTable,2)
				commonLib.compileKernel()
				command = "net init;load -b tftp://"+commonLib.getHostIP()+"/vmlinux.bin "+loadingAddress+";go\n" 
				print command
				mySerialPortInterface.writeCommand(command)		
		
				ret = mySerialPortInterface.analyzeInputStream('errorDetection')

				if ret != 0:
					NodeCollection[i].parents[0] = 1					
				saveCurrentStatus(2,NodeCollection,len(NodeCollection),mySerialPortInterface,i+1)
	
def saveCurrentStatus(partNumber,NodeCollection,numberOfInitCalls,mySerialPortInterface,positionInTheLoop=2):
	pwd = os.getcwd()
	f = open(pwd+'/restore'+str(mySerialPortInterface.port),'w')
	f.write(str(partNumber)+"\n")
	f.write(str(numberOfInitCalls)+"\n")
	if partNumber == 2:
		f.write(str(positionInTheLoop)+'\n')
	f.write(str(mySerialPortInterface.port)+"\n")
	serializedList = pickle.dumps(NodeCollection)
	f.write(serializedList)
	f.close()

	#Now write the status of this serial	
	f = open(pwd+'/../status'+str(mySerialPortInterface.port),'w')
	if partNumber == 1 :
		f.write("Currently trying to find dependencies of node "+str(len(NodeCollection))+" out of "+str(numberOfInitCalls))	
	else:
		f.write("Currently trying to find last dependencies of the initcalls")

def getNumberOfInitCalls(mySerialPortInterface):
	loadingAddress = commonLib.getOptions()['loadingAddress']
	#To do so we use the regular boot:
	node = commonLib.Node(1)
	deltaTable = generateDeltaTableInOrderToPutNodeAtPosition(node,0,4)		
	commonLib.generateKernelCode('REGULAR','YES',deltaTable,1)
	commonLib.compileKernel()
	command = "net init; load -b tftp://"+commonLib.getHostIP()+"/vmlinux.bin "+loadingAddress+";go\n" 
	mySerialPortInterface.writeCommand(command)		
	ret = mySerialPortInterface.analyzeInputStream('getNbrOfInitCalls')
	
	print "There are "+ str(ret) + " initcalls."
	return ret	

def cleanNodeCollection(NodeCollection):
	for Node in NodeCollection:
		if Node.parents[0] != -1:
			for i, parent in enumerate(Node.parents):
				if parent == -1:
					Node.parents.remove(Node.parents[i])
